package com.aizuda.boot.modules.file.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.aizuda.boot.modules.business.contract.dto.SingleResponse;
import com.aizuda.boot.modules.business.contract.dto.UserInfo;
import com.aizuda.boot.modules.common.constant.enums.ResponseCodeEnum;
import com.aizuda.boot.modules.file.domain.entity.FileInfo;
import com.aizuda.boot.modules.file.domain.vo.FileDownloadVO;
import com.aizuda.boot.modules.file.domain.vo.FileMetadataVO;
import com.aizuda.boot.modules.file.domain.vo.FileUploadVO;
import com.aizuda.boot.modules.file.domain.vo.FileVO;
import com.aizuda.boot.modules.file.enums.FileFolderTypeEnum;
import com.aizuda.boot.modules.file.mapper.FileInfoMapper;
import com.aizuda.boot.modules.file.util.WpsConversionUtil;
import com.aizuda.boot.modules.third.wps.domain.entity.FileHistory;
import com.aizuda.boot.modules.third.wps.mapper.FileHistoryMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.aizuda.boot.modules.file.util.FileResponseUtil;
import com.aizuda.boot.modules.file.util.FileConversionUtil;
import com.aizuda.boot.modules.business.contract.util.AuthUtil;
import com.aizuda.boot.modules.file.service.IFileConversionCacheService;
import com.aizuda.boot.modules.file.domain.entity.FileConversionCache;
import cn.hutool.http.Header;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
@Slf4j
public class FileInfoService extends ServiceImpl<FileInfoMapper, FileInfo> implements IFileInfoService {
    /**
     * 文件名最大长度
     */
    private static final int FILE_NAME_MAX_LENGTH = 500;
    public static final String UPLOAD_MAPPING = "/uploads";
    private String uploadPath;

    @Value("${file.storage.local.upload-path:#{null}}")
    public void setUploadPath(String uploadPath) {
        this.uploadPath = StrUtil.isBlank(uploadPath) ? System.getProperty("java.io.tmpdir") + File.separator : uploadPath;
    }

    @Value("${file.storage.local.url-prefix}")
    private String urlPrefix;

    @Value("${server.servlet.context-path:/}")
    private String contextPath;

    @Value("${server.port}")
    private String port;

    @Resource
    private FileInfoMapper fileInfoMapper;
    @Resource
    private FileHistoryMapper fileHistoryMapper;
    @Resource
    private WpsConversionUtil wpsConversionUtil;
    @Resource
    private IFileConversionCacheService conversionCacheService;


    @PostConstruct
    public void initUrlPrefix() {
        if (StrUtil.isNotBlank(urlPrefix)) {
            return;
        }

        String localhostIp = NetUtil.getLocalhostStr();
        String finalContextPath = contextPath.startsWith(File.separator) ? contextPath : File.separator + contextPath;
        if (finalContextPath.endsWith(File.separator)) {
            finalContextPath = finalContextPath.substring(0, finalContextPath.length() - 1);
        }
        urlPrefix = "http://" + localhostIp + ":" + port + finalContextPath + UPLOAD_MAPPING;
        urlPrefix = urlPrefix.endsWith(File.separator) ? urlPrefix : urlPrefix + File.separator;
    }


    /**
     * 文件上传服务
     *
     * @param file
     * @param folderType 文件夹类型
     * @return
     */
    public SingleResponse<FileUploadVO> fileUpload(MultipartFile file, Integer folderType, UserInfo currentUser) {
        FileFolderTypeEnum folderTypeEnum = FileFolderTypeEnum.getByValue(folderType);
        if (ObjectUtil.isNull(folderType)) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件夹错误");
        }

        if (null == file || file.getSize() == 0) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "上传文件不能为空");
        }

        // 校验文件名称
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "上传文件名称不能为空");
        }

        if (originalFilename.length() > FILE_NAME_MAX_LENGTH) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件名称最大长度为：" + FILE_NAME_MAX_LENGTH);

        }

        // todo 校验文件大小以及安全性


        // 进行上传
        SingleResponse<FileUploadVO> response = this.upload(file, folderTypeEnum.getFolder());
        if (!response.isSuccess()) {
            return response;
        }
        // 上传成功 保存记录数据库
        FileUploadVO uploadVO = response.getData();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFolderType(folderTypeEnum.getValue());
        fileInfo.setFileName(originalFilename);
        fileInfo.setFileSize(uploadVO.getFileSize());
        fileInfo.setFileKey(uploadVO.getFileKey());
        fileInfo.setFileType(uploadVO.getFileType());
        fileInfo.setCreatorId(currentUser == null ? null : currentUser.getEmployeeId());

        fileInfoMapper.insert(fileInfo);

        // 将fileId 返回给前端
        uploadVO.setFileId(fileInfo.getFileId());

        return response;
    }

    @Override
    public SingleResponse<FileUploadVO> fileUpload(String sourceUrl, Integer folderType, UserInfo currentUser) {
        FileFolderTypeEnum folderTypeEnum = FileFolderTypeEnum.getByValue(folderType);
        // 进行上传
        SingleResponse<FileUploadVO> response = this.upload(sourceUrl, folderTypeEnum.getFolder());
        if (!response.isSuccess()) {
            return response;
        }
        // 上传成功 保存记录数据库
        FileUploadVO uploadVO = response.getData();
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFolderType(folderTypeEnum.getValue());
        fileInfo.setFileName(response.getData().getFileName());
        fileInfo.setFileSize(uploadVO.getFileSize());
        fileInfo.setFileKey(uploadVO.getFileKey());
        fileInfo.setFileType(uploadVO.getFileType());
        fileInfo.setCreatorId(currentUser == null ? null : currentUser.getEmployeeId());

        fileInfoMapper.insert(fileInfo);

        // 将fileId 返回给前端
        uploadVO.setFileId(fileInfo.getFileId());

        return response;
    }

    /**
     * 根据远端Url文件上传
     *
     * @param sourceUrl
     * @param path
     * @return
     */
    public SingleResponse<FileUploadVO> upload(String sourceUrl, String path) {
        if (StringUtils.isBlank(sourceUrl)) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "源文件URL不能为空");
        }
        if (!path.endsWith(File.separator)) {
            path = path + File.separator;
        }
        String filePath = uploadPath + path;
        File directory = new File(filePath);
        if (!directory.exists()) {
            directory.mkdirs();
        }
        FileNameUtil.getPrefix(path);
        String fileName = FileNameUtil.getName(sourceUrl);
        fileName = StrUtil.subBefore(fileName, "?", true);
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
        String newFileName = uuid + "_" + time;
        String fileType = FilenameUtils.getExtension(fileName);
        if (StrUtil.isNotEmpty(fileType)) {
            newFileName = newFileName + "." + fileType;
        }
        String fileKey = path + newFileName;
        FileUploadVO fileUploadVO = new FileUploadVO();
        try {
            byte[] in = HttpUtil.downloadBytes(sourceUrl);
            if (in == null) {
                return SingleResponse.failure(ResponseCodeEnum.ERROR.getCode(), "无法获取远程文件流");
            }
            File file = FileUtil.newFile(filePath + newFileName);
            //写入文件
            FileUtil.writeBytes(in, file);
            long size = in.length;
            file.setReadable(true, false);
            file.setWritable(true, true);
            file.setExecutable(false, false);

            fileUploadVO.setFileUrl(this.generateFileUrl(fileKey));
            fileUploadVO.setFileName(fileName);
            fileUploadVO.setFileKey(fileKey);
            fileUploadVO.setFileSize(size);
            fileUploadVO.setFileType(fileType);
        } catch (Exception e) {
            if (FileUtil.exist(filePath + newFileName)) {
                FileUtil.del(filePath + newFileName);
            }
            log.error("远程文件上传异常", e);
            return SingleResponse.failure(ResponseCodeEnum.ERROR.getCode(), "远程文件上传失败");
        }
        return SingleResponse.of(fileUploadVO);
    }


    /**
     * 文件上传
     *
     * @param multipartFile
     * @param path
     * @return
     */
    public SingleResponse<FileUploadVO> upload(MultipartFile multipartFile, String path) {
        if (null == multipartFile) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "上传文件不能为空");
        }
        String filePath = uploadPath + path;
        File directory = new File(filePath);
        if (!directory.exists()) {
            // 目录不存在，新建
            directory.mkdirs();
        }
        if (!path.endsWith(File.separator)) {
            path = path + File.separator;
        }


        FileUploadVO fileUploadVO = new FileUploadVO();
        //原文件名
        String originalFileName = multipartFile.getOriginalFilename();
        //新文件名
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
        String newFileName = uuid + "_" + time;
        String fileType = FilenameUtils.getExtension(originalFileName);
        if (StrUtil.isNotEmpty(fileType)) {
            newFileName = newFileName + "." + fileType;
        }
        //生成文件key
        String fileKey = path + newFileName;
        //创建文件
        try {


            File file = FileUtil.newFile(filePath + newFileName);
            long size = multipartFile.getSize();
            multipartFile.transferTo(file);

            // 设置文件权限为 644（所有用户可读，只有所有者可写）
            // 这样 nginx 用户就可以读取文件了
            file.setReadable(true, false);  // false 表示所有用户可读
            file.setWritable(true, true);   // true 表示只有所有者可写
            file.setExecutable(false, false); // 文件不需要执行权限

            fileUploadVO.setFileUrl(this.generateFileUrl(fileKey));
            fileUploadVO.setFileName(originalFileName);
            fileUploadVO.setFileKey(fileKey);
            fileUploadVO.setFileSize(size);
            fileUploadVO.setFileType(FilenameUtils.getExtension(originalFileName));
        } catch (IOException e) {
            if (FileUtil.exist(filePath + newFileName)) {
                FileUtil.del(filePath + newFileName);
            }

            log.error("文件上传异常", e);
            return SingleResponse.failure(ResponseCodeEnum.ERROR.getCode(), "上传失败");
        }
        return SingleResponse.of(fileUploadVO);
    }

    /**
     * 生成fileUrl地址
     *
     * @param filePath
     * @return
     */
    public String generateFileUrl(String filePath) {
        return urlPrefix + filePath;
    }


    /**
     * 根据文件绝对路径 获取文件URL
     * 支持单个 key 逗号分隔的形式
     *
     * @param fileKeys
     * @return
     */
    public SingleResponse<String> getFileUrl(String fileKeys) {

        if (StringUtils.isBlank(fileKeys)) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), ResponseCodeEnum.PARAMETER_ERROR.getMsg());
        }

        List<String> fileKeyArray = StrUtil.split(fileKeys, ',');
        List<String> fileUrlList = new ArrayList<>(fileKeyArray.size());
        for (String fileKey : fileKeyArray) {
            SingleResponse<String> fileUrlResponse = getLocalFileUrl(fileKey);
            if (fileUrlResponse.isSuccess()) {
                fileUrlList.add(fileUrlResponse.getData());
            }
        }
        return SingleResponse.of(StrUtil.join(",", fileUrlList));
    }

    /**
     * 获取文件Url
     *
     * @param fileKey
     * @return
     */
    private SingleResponse<String> getLocalFileUrl(String fileKey) {
        if (StringUtils.isBlank(fileKey)) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件不存在，key为空");
        }

        String fileUrl = this.generateFileUrl(fileKey);
        return SingleResponse.of(fileUrl);
    }

    /**
     * 文件下载
     *
     * @param fileKey
     * @return
     */
    public SingleResponse<FileDownloadVO> getDownloadFile(String fileKey, String userAgent) {
        //根据fileKey查询文件信息
        String fileName;
        FileInfo fileInfo = fileInfoMapper.selectOne(new QueryWrapper<FileInfo>().eq("file_key", fileKey));
        if (fileInfo == null) {
            //如果原始文件表中不存在,则查询历史文件表
            FileHistory fileHistory = fileHistoryMapper.selectOne(new LambdaQueryWrapper<FileHistory>().eq(FileHistory::getFileKey, fileKey));
            if (ObjectUtil.isNull(fileHistory)) {
                return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件不存在");
            }
            fileName = fileHistory.getFileName();
        } else {
            fileName = fileInfo.getFileName();
        }
        // 根据文件服务类 获取对应文件服务 查询 url
        SingleResponse<FileDownloadVO> download = download(fileKey);


        if (download.isSuccess()) {
            download.getData().getMetadata().setFileName(fileName);
        }
        return download;
    }

    /**
     * 文件下载
     *
     * @param fileKey
     * @return
     */
    private SingleResponse<FileDownloadVO> download(String fileKey) {
        String filePath = uploadPath + fileKey;
        File localFile = new File(filePath);
        InputStream in = null;
        try {
            in = Files.newInputStream(localFile.toPath());
            // 输入流转换为字节流
            byte[] buffer = FileCopyUtils.copyToByteArray(in);
            FileDownloadVO fileDownloadVO = new FileDownloadVO();
            fileDownloadVO.setData(buffer);

            FileMetadataVO fileMetadataDTO = new FileMetadataVO();
            fileMetadataDTO.setFileName(localFile.getName());
            fileMetadataDTO.setFileSize(localFile.length());
            fileMetadataDTO.setFileFormat(FilenameUtils.getExtension(localFile.getName()));
            fileDownloadVO.setMetadata(fileMetadataDTO);

            return SingleResponse.of(fileDownloadVO);
        } catch (IOException e) {
            log.error("文件下载-发生异常：", e);
            return SingleResponse.failure(ResponseCodeEnum.ERROR.getCode(), "文件下载失败");
        } finally {
            try {
                // 关闭输入流
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                log.error("文件下载-发生异常：", e);
            }
        }
    }

    /**
     * 根据文件id 获取文件信息
     *
     * @param fileId 文件ID
     * @return 文件信息
     */
    public SingleResponse<FileVO> getFileInfoById(String fileId) {

        //根据fileId查询文件原始信息 如果不存在则返回参数异常信息


        FileInfo fileInfo = fileInfoMapper.selectById(fileId);
        if (fileInfo == null) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件不存在");
        }
        FileVO fileVO;
        //查询最新版本信息
        FileHistory fileHistory = fileHistoryMapper.selectOne(new LambdaQueryWrapper<FileHistory>()
                .eq(FileHistory::getFileId, fileId)
                .orderByDesc(FileHistory::getVersion)
                .last("limit 1")
        );

        //最新版本信息不存在则返回文件原始信息
        if (fileHistory == null) {
            fileVO = FileVO.builder().build();
            BeanUtil.copyProperties(fileInfo, fileVO);
            return SingleResponse.of(fileVO);
        }

        //如果最新版本信息存在则返回最新版本信息
        fileVO = FileVO.builder()
                .fileId(fileHistory.getFileId())
                .folderType(fileInfo.getFolderType())
                .fileName(fileHistory.getFileName())
                .fileKey(fileHistory.getFileKey())
                .fileSize(fileHistory.getFileSize())
                .fileType(fileInfo.getFileType())
                .createTime(fileHistory.getCreateTime())
                .creatorId(Long.valueOf(fileHistory.getCreatorId()))
                .build();

        return SingleResponse.of(fileVO);
    }

    /**
     * 复制文件
     * 用于模板文件复制场景，避免原文件被修改
     *
     * @param sourceFileId 源文件ID
     * @param folderType   目标文件夹类型
     * @param currentUser  当前用户
     * @return 新文件信息
     */
    public SingleResponse<FileUploadVO> copyFile(String sourceFileId, Integer folderType, UserInfo currentUser) {
        // 1. 查询源文件信息
        FileInfo sourceFile = fileInfoMapper.selectById(sourceFileId);
        if (sourceFile == null) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "源文件不存在");
        }

        // 2. 查询源文件的最新版本信息（如果有）
        FileHistory latestHistory = fileHistoryMapper.selectOne(new LambdaQueryWrapper<FileHistory>()
                .eq(FileHistory::getFileId, sourceFileId)
                .orderByDesc(FileHistory::getVersion)
                .last("limit 1")
        );

        // 确定要复制的文件key和文件名
        String sourceFileKey = latestHistory != null ? latestHistory.getFileKey() : sourceFile.getFileKey();
        String sourceFileName = latestHistory != null ? latestHistory.getFileName() : sourceFile.getFileName();
        Long sourceFileSize = latestHistory != null ? latestHistory.getFileSize() : sourceFile.getFileSize();

        // 3. 读取源文件内容
        String sourceFilePath = uploadPath + sourceFileKey;
        File sourceFileObj = new File(sourceFilePath);
        if (!sourceFileObj.exists()) {
            log.error("文件复制失败: 数据库记录存在但物理文件不存在. fileId={}, fileKey={}, filePath={}",
                    sourceFileId, sourceFileKey, sourceFilePath);
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(),
                    "源文件不存在于文件系统，文件可能已被删除或移动。fileKey: " + sourceFileKey);
        }

        // 4. 生成新的文件路径
        FileFolderTypeEnum folderTypeEnum = FileFolderTypeEnum.getByValue(folderType);
        if (folderTypeEnum == null) {
            return SingleResponse.failure(ResponseCodeEnum.PARAMETER_ERROR.getCode(), "文件夹类型错误");
        }

        String targetPath = folderTypeEnum.getFolder();
        if (!targetPath.endsWith(File.separator)) {
            targetPath = targetPath + File.separator;
        }

        // 确保目标目录存在
        String targetDirPath = uploadPath + targetPath;
        File targetDir = new File(targetDirPath);
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }

        // 5. 生成新文件名
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        String time = LocalDateTimeUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN);
        String newFileName = uuid + "_" + time;
        String fileType = sourceFile.getFileType();
        if (StrUtil.isNotEmpty(fileType)) {
            newFileName = newFileName + "." + fileType;
        }

        String newFileKey = targetPath + newFileName;
        File targetFile = new File(targetDirPath + newFileName);

        try {
            // 6. 复制文件
            Files.copy(sourceFileObj.toPath(), targetFile.toPath());

            // 7. 创建新的文件信息记录
            FileInfo newFileInfo = new FileInfo();
            newFileInfo.setFolderType(folderTypeEnum.getValue());
            newFileInfo.setFileName(sourceFileName);  // 使用最新版本的文件名
            newFileInfo.setFileSize(sourceFileSize);  // 使用最新版本的文件大小
            newFileInfo.setFileKey(newFileKey);
            newFileInfo.setFileType(sourceFile.getFileType());
            newFileInfo.setCreatorId(currentUser == null ? null : currentUser.getEmployeeId());

            fileInfoMapper.insert(newFileInfo);

            // 8. 构建返回结果
            FileUploadVO uploadVO = new FileUploadVO();
            uploadVO.setFileId(newFileInfo.getFileId());
            uploadVO.setFileUrl(this.generateFileUrl(newFileKey));
            uploadVO.setFileName(newFileInfo.getFileName());
            uploadVO.setFileKey(newFileKey);
            uploadVO.setFileSize(newFileInfo.getFileSize());
            uploadVO.setFileType(newFileInfo.getFileType());

            log.info("文件复制成功: 源文件ID={}, 新文件ID={}", sourceFileId, newFileInfo.getFileId());

            return SingleResponse.of(uploadVO);

        } catch (IOException e) {
            log.error("文件复制失败: sourceFileId={}", sourceFileId, e);
            // 如果复制失败，删除可能已创建的文件
            if (targetFile.exists()) {
                targetFile.delete();
            }
            return SingleResponse.failure(ResponseCodeEnum.ERROR.getCode(), "文件复制失败");
        }
    }

    @Override
    public List<String> getFileNamesByIds(List<Long> ids) {
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }
        return this.list(Wrappers.<FileInfo>lambdaQuery()
                        .in(FileInfo::getFileId, ids)
                        .select(FileInfo::getFileName))
                .stream().map(FileInfo::getFileName).toList();
    }

    @Override
    public void downloadContractByFileId(String fileId, String format, HttpServletRequest request, HttpServletResponse response) throws IOException {
        log.info("开始根据文件ID下载合同文件，fileId: {}, format: {}", fileId, format);
        
        try {
            // 1. 根据fileId获取文件信息
            SingleResponse<FileVO> fileInfoResult = getFileInfoById(fileId);
            if (!fileInfoResult.isSuccess() || fileInfoResult.getData() == null) {
                log.warn("根据fileId获取文件信息失败，fileId: {}", fileId);
                FileResponseUtil.write(response, SingleResponse.of("文件不存在或获取文件信息失败"));
                return;
            }
            
            // 2. 获取fileKey
            String fileKey = fileInfoResult.getData().getFileKey();
            if (fileKey == null || fileKey.trim().isEmpty()) {
                log.warn("文件Key为空，fileId: {}", fileId);
                FileResponseUtil.write(response, SingleResponse.of("文件Key不存在"));
                return;
            }
            
            log.info("根据fileId获取到fileKey: {}", fileKey);
            
            // 3. 调用现有的下载逻辑
            downloadContractByFileKey(fileKey, format, request, response);
            
        } catch (Exception e) {
            log.error("根据fileId下载合同文件异常，fileId: {}, format: {}", fileId, format, e);
            FileResponseUtil.write(response, SingleResponse.of("下载文件失败: " + e.getMessage()));
        }
    }

    @Override
    public void downloadContractByFileKey(String fileKey, String format, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String userAgent = request.getHeader(Header.USER_AGENT.getValue());
        
        // 验证format参数
        if (!"docx".equalsIgnoreCase(format) && !"pdf".equalsIgnoreCase(format)) {
            FileResponseUtil.write(response, SingleResponse.of("格式参数错误，只支持docx或pdf"));
            return;
        }
        
        // 获取原始文件
        SingleResponse<FileDownloadVO> downloadFileResult = getDownloadFile(fileKey, userAgent);
        if (!downloadFileResult.isSuccess()) {
            FileResponseUtil.write(response, downloadFileResult);
            return;
        }
        
        FileDownloadVO fileDownloadVO = downloadFileResult.getData();
        String originalFileName = fileDownloadVO.getMetadata().getFileName();
        byte[] originalData = fileDownloadVO.getData();
        
        // 判断原始文件格式
        boolean isOriginalPdf = FileConversionUtil.isPdfFile(originalFileName);
        boolean isOriginalDocx = FileConversionUtil.isDocxFile(originalFileName);
        
        if (!isOriginalPdf && !isOriginalDocx) {
            FileResponseUtil.write(response, SingleResponse.of("该文件不是PDF或DOCX格式，无法处理"));
            return;
        }
        
        byte[] finalData = originalData;
        String finalFileName = originalFileName;
        
        try {
            if ("pdf".equalsIgnoreCase(format)) {
                // 请求PDF格式
                if (isOriginalPdf) {
                    // 原文件是PDF，直接下载
                    log.info("原文件是PDF格式，直接下载");
                } else {
                    // 原文件是DOCX，需要转换为PDF - 使用缓存机制
                    log.info("原文件是DOCX格式，检查转换缓存或使用WPS API转换为PDF");
                    
                    // 计算源文件MD5（用于缓存验证）
                    String sourceFileMd5 = SecureUtil.md5(new ByteArrayInputStream(originalData));
                    
                    try {
                        // 使用缓存服务获取或创建PDF
                        String cachedFileKey = conversionCacheService.getOrCreateWordToPdfCache(
                            null, // 这里没有fileId，使用fileKey作为标识
                            fileKey,
                            sourceFileMd5,
                            (sourceKey) -> {
                                try {
                                    // 获取文件的访问URL
                                    SingleResponse<String> fileUrlResult = getFileUrl(sourceKey);
                                    if (!fileUrlResult.isSuccess() || fileUrlResult.getData() == null) {
                                        throw new RuntimeException("无法获取文件访问地址");
                                    }
                                    
                                    String fileUrl = fileUrlResult.getData();
                                    log.info("文件访问地址: {}", fileUrl);
                                    
                                    // 使用WPS API转换
                                    String pdfUrl = wpsConversionUtil.convertWordToPdf(fileUrl, originalFileName);
                                    byte[] pdfData = wpsConversionUtil.downloadPdfFile(pdfUrl);
                                    String pdfFileName = changeFileExtension(originalFileName, "pdf");
                                    
                                    // 保存PDF文件到文件系统
                                    return savePdfFile(pdfData, pdfFileName);
                                } catch (Exception e) {
                                    log.error("WPS转换失败", e);
                                    throw new RuntimeException("文件转换失败: " + e.getMessage());
                                }
                            }
                        );
                        
                        // 从缓存的文件Key获取PDF数据
                        SingleResponse<FileDownloadVO> cachedPdfResult = getDownloadFile(cachedFileKey, userAgent);
                        if (cachedPdfResult.isSuccess() && cachedPdfResult.getData() != null) {
                            finalData = cachedPdfResult.getData().getData();
                            finalFileName = cachedPdfResult.getData().getMetadata().getFileName();
                            log.info("使用缓存的PDF文件，缓存文件Key: {}", cachedFileKey);
                        } else {
                            throw new RuntimeException("获取缓存PDF文件失败");
                        }
                        
                    } catch (Exception e) {
                        log.error("PDF缓存处理失败，回退到直接转换: {}", e.getMessage());
                        
                        // 缓存失败时回退到原来的逻辑
                        SingleResponse<String> fileUrlResult = getFileUrl(fileKey);
                        if (!fileUrlResult.isSuccess() || fileUrlResult.getData() == null) {
                            FileResponseUtil.write(response, SingleResponse.of("无法获取文件访问地址"));
                            return;
                        }
                        
                        String fileUrl = fileUrlResult.getData();
                        String pdfUrl = wpsConversionUtil.convertWordToPdf(fileUrl, originalFileName);
                        finalData = wpsConversionUtil.downloadPdfFile(pdfUrl);
                        finalFileName = changeFileExtension(originalFileName, "pdf");
                    }
                }
            } else {
                // 请求DOCX格式
                // 先检查是否为管理员
                if (!AuthUtil.isSuperAdmin()) {
                    FileResponseUtil.write(response, SingleResponse.of("只有管理员才可以下载此版本文件"));
                    return;
                }
                
                if (isOriginalDocx) {
                    // 原文件是DOCX，直接下载
                    log.info("原文件是DOCX格式，管理员直接下载");
                } else {
                    // 不再支持PDF转DOCX功能
                    FileResponseUtil.write(response, SingleResponse.of("不支持PDF转换为DOCX格式"));
                    return;
                }
            }
            
            // 设置下载响应头
            FileResponseUtil.setDownloadFileHeader(response, finalFileName, (long) finalData.length);
            
            // 下载文件
            response.getOutputStream().write(finalData);
            
        } catch (Exception e) {
            log.error("合同文件转换或下载失败", e);
            FileResponseUtil.write(response, SingleResponse.of("文件处理失败：" + e.getMessage()));
        }
    }

    @Override
    public void batchDownloadFiles(List<String> fileKeys, HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (fileKeys == null || fileKeys.isEmpty()) {
            response.setContentType("application/json;charset=UTF-8");
            FileResponseUtil.write(response, SingleResponse.of("文件列表不能为空"));
            return;
        }

        String userAgent = request.getHeader(Header.USER_AGENT.getValue());

        // 用于跟踪已添加的文件名，避免重复
        Set<String> addedFileNames = new HashSet<>();

        // 设置ZIP响应头
        String zipFileName = "batch_download_" + System.currentTimeMillis() + ".zip";
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"" + zipFileName + "\"");
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Expires", "0");

        // 创建ZIP输出流
        try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
            // 设置压缩级别
            zipOut.setLevel(ZipOutputStream.DEFLATED);

            // 处理每个文件
            int successCount = 0;
            for (String fileKey : fileKeys) {
                try {
                    SingleResponse<FileDownloadVO> downloadFileResult = getDownloadFile(fileKey, userAgent);

                    if (downloadFileResult.isSuccess() && downloadFileResult.getData() != null) {
                        FileDownloadVO fileDownloadVO = downloadFileResult.getData();
                        String fileName = fileDownloadVO.getMetadata().getFileName();

                        // 处理重复文件名
                        fileName = generateUniqueFileName(fileName, addedFileNames);
                        addedFileNames.add(fileName);

                        // 添加文件到ZIP
                        ZipEntry zipEntry = new ZipEntry(fileName);
                        zipEntry.setTime(System.currentTimeMillis());
                        zipOut.putNextEntry(zipEntry);
                        zipOut.write(fileDownloadVO.getData());
                        zipOut.closeEntry();
                        zipOut.flush();

                        successCount++;
                    }
                } catch (Exception e) {
                    // 记录错误但继续处理其他文件
                    log.error("处理文件失败，fileKey: {}", fileKey, e);
                }
            }

            // 如果没有成功添加任何文件，添加一个说明文件
            if (successCount == 0) {
                ZipEntry errorEntry = new ZipEntry("error.txt");
                zipOut.putNextEntry(errorEntry);
                zipOut.write("未能成功下载任何文件，请检查文件Key是否正确。".getBytes("UTF-8"));
                zipOut.closeEntry();
            }

            // 完成ZIP文件
            zipOut.finish();
            zipOut.flush();
        } catch (Exception e) {
            log.error("批量下载失败", e);
            // 发生异常时，如果还没有写入响应，则返回错误信息
            if (!response.isCommitted()) {
                response.reset();
                response.setContentType("application/json;charset=UTF-8");
                FileResponseUtil.write(response, SingleResponse.of("批量下载失败：" + e.getMessage()));
            }
        }
    }

    /**
     * 生成唯一的文件名，避免在ZIP中出现重复
     *
     * @param originalFileName  原始文件名
     * @param existingFileNames 已存在的文件名集合
     * @return 唯一的文件名
     */
    private String generateUniqueFileName(String originalFileName, Set<String> existingFileNames) {
        if (!existingFileNames.contains(originalFileName)) {
            return originalFileName;
        }

        String nameWithoutExt = originalFileName;
        String extension = "";

        int dotIndex = originalFileName.lastIndexOf('.');
        if (dotIndex > 0) {
            nameWithoutExt = originalFileName.substring(0, dotIndex);
            extension = originalFileName.substring(dotIndex);
        }

        int counter = 1;
        String newFileName;
        do {
            newFileName = nameWithoutExt + "_" + counter + extension;
            counter++;
        } while (existingFileNames.contains(newFileName));

        return newFileName;
    }

    /**
     * 更改文件扩展名
     * 
     * @param originalFileName 原始文件名
     * @param newExtension 新扩展名（不包含点）
     * @return 新文件名
     */
    private String changeFileExtension(String originalFileName, String newExtension) {
        int dotIndex = originalFileName.lastIndexOf('.');
        if (dotIndex > 0) {
            return originalFileName.substring(0, dotIndex) + "." + newExtension;
        } else {
            return originalFileName + "." + newExtension;
        }
    }

    /**
     * 保存PDF文件到文件系统和数据库
     * 
     * @param pdfData PDF文件数据
     * @param pdfFileName PDF文件名
     * @return 转换结果
     */
    private IFileConversionCacheService.ConversionResult savePdfFile(byte[] pdfData, String pdfFileName) {
        try {
            // 生成文件Key和保存路径
            String timeFolder = LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy/MM/dd");
            String fileKey = timeFolder + "/" + UUID.randomUUID().toString() + "_" + pdfFileName;
            String filePath = uploadPath + fileKey;
            
            // 确保目录存在
            File targetFile = new File(filePath);
            File parentDir = targetFile.getParentFile();
            if (!parentDir.exists()) {
                parentDir.mkdirs();
            }
            
            // 写入文件数据
            try (FileOutputStream fos = new FileOutputStream(targetFile)) {
                fos.write(pdfData);
                fos.flush();
            }
            
            // 保存文件信息到数据库
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFolderType(2); // 合同文件夹
            fileInfo.setFileName(pdfFileName);
            fileInfo.setFileSize((long) pdfData.length);
            fileInfo.setFileKey(fileKey);
            fileInfo.setFileType("pdf");
            fileInfo.setCreatorId(null); // 系统转换的文件
            
            fileInfoMapper.insert(fileInfo);
            
            log.info("转换后的PDF文件保存成功，文件Key: {}, 文件大小: {}", fileKey, fileInfo.getFileSize());
            
            return new IFileConversionCacheService.ConversionResult(
                fileKey, 
                fileInfo.getFileId().toString(), 
                fileInfo.getFileSize()
            );
            
        } catch (IOException e) {
            throw new RuntimeException("保存PDF文件失败: " + e.getMessage(), e);
        }
    }
}
